package com.wcy.recommend;


import com.wcy.entity.UserCollection;
import com.wcy.entity.UserLearningHistory;
import com.wcy.entity.UserRating;

import java.util.*;

//wcy编写，基于用户CF的可调权重算法
public class MyCFRecommend {
    //创建用户矩阵
    //Map<user_id,Map<resource_id,value>>
    //     1 2 3 4 5 6 7 8 9 10
    //张三
    //李四

    //用户矩阵Map<user_id,Map<resource_id,value>>
    private final Map<Integer,Map<Integer,Double>> userMatrix = new HashMap<>();
    //评分|收藏|历史
    //weight weight2
    private double weight=0.33;//表示计算权重偏向评分还是收藏
    private double weight1 =0.66;//在收藏内再加权重
    private double threshold = 0.5;//相似度高于阈值即视为相同用户
    private int maxRecommendNum = 8;
    public MyCFRecommend(double weight, double weight1, double threshold, int maxRecommendNum){
        this.weight = weight;
        this.weight1 = weight1;
        this.threshold = threshold;
        this.maxRecommendNum = maxRecommendNum;
    }
    //学习资源推荐网站
    //创建用户矩阵
    public void createUserMatrix(List<UserRating> ratings, List<UserCollection> userCollections, List<UserLearningHistory> historyList) {
        //预处理数据，将数据转换成用户行为矩阵，进行归一化后再变成5分制
        for (UserRating rating : ratings) {
            if (!userMatrix.containsKey(rating.getUserId())) {
                userMatrix.put(rating.getUserId(), new HashMap<>());
            }
            userMatrix.get(rating.getUserId()).put(rating.getResourceId(), rating.getRatingValue() * weight);
        }
        System.out.println("1:"+userMatrix);
        for (UserCollection collection : userCollections) {
            if (!userMatrix.containsKey(collection.getUserId())) {
                userMatrix.put(collection.getUserId(), new HashMap<>());
            }
            Map<Integer, Double> userMap = userMatrix.get(collection.getUserId());
            if (!userMap.containsKey(collection.getResourceId()))
                userMap.put(collection.getResourceId(), 5 * (weight1 - weight));
        }
        System.out.println("2:"+userMatrix);
        //50作为满分5分指标进行归一化，将相同history进行叠加
        //用户id,<资源id,总时间>
        Map<Integer, Map<Integer, Integer>> historyMap = new HashMap<>();
        for (UserLearningHistory history : historyList) {
            if (!historyMap.containsKey(history.getUserId()))
                historyMap.put(history.getUserId(), new HashMap<>());
            Map<Integer, Integer> resourceIdTimeMap = historyMap.get(history.getUserId());
            if (resourceIdTimeMap.containsKey(history.getResourceId())) {
                resourceIdTimeMap.put(history.getResourceId(), Math.min(resourceIdTimeMap.get(history.getResourceId()) + history.getStudyDuration(),50));
            } else {
                resourceIdTimeMap.put(history.getResourceId(), Math.min(history.getStudyDuration(),50));
            }
        }

        for (Map.Entry<Integer, Map<Integer, Integer>> entry : historyMap.entrySet()) {
            Integer userId = entry.getKey();
            if(!userMatrix.containsKey(userId))
                userMatrix.put(userId, new HashMap<>());
            for(Map.Entry<Integer, Integer> entry1 : entry.getValue().entrySet()){
                Map<Integer, Double> userMap = userMatrix.get(userId);
                if(!userMap.containsKey(entry1.getKey()))
                    userMap.put(entry1.getKey(), (double)entry1.getValue()/10d*(1d- weight1));
                else
                    userMap.put(entry1.getKey(), userMap.get(entry1.getKey())+(double)entry1.getValue()/10d*(1d- weight1));
                System.out.println(userId+"u:"+userMap+" "+entry1.getKey()+" "+entry1.getValue()+" "+(double)entry1.getValue()/10d*(1d- weight1));
            }
        }
        System.out.println(historyMap);
        System.out.println(userMatrix);
    }

    //为指定userId进行推荐计算
    public List<Integer> recommend(Integer userId) {
        if (userId == null) {
            System.out.println("用户不能为空");
            return new ArrayList<>();
        }
        Map<Integer, Double> userMap = userMatrix.get(userId);
        if (userMap == null) {
            System.out.println("用户无特征数据");
            return new ArrayList<>();
        }

        //推荐列表,使用Set防止重复推荐
        Set<Integer> result = new TreeSet<>();

        for (Map.Entry<Integer, Map<Integer, Double>> entry : userMatrix.entrySet()) {
            if (entry.getKey().equals(userId)) {
                //自身不参与计算，跳过
                continue;
            }
            double sumX = 0.0;
            double sumY = 0.0;
            double sumX2 = 0.0;
            double sumY2 = 0.0;
            double sumXY = 0.0;
            int n = 0;

            //用于存放该用户有但是指定用户没有的资源
            List<Integer> othersHasItems = new ArrayList<>();
            for (Map.Entry<Integer, Double> item : entry.getValue().entrySet()) {
                if (userMap.containsKey(item.getKey())) {
                    //找出需求用户样本用户观看的相同的电影
                    sumX += userMap.get(item.getKey());
                    sumY += item.getValue();
                    sumXY += userMap.get(item.getKey()) * item.getValue();
                    sumX2 += userMap.get(item.getKey()) * userMap.get(item.getKey());
                    sumY2 += item.getValue() * item.getValue();
                    n++;
                }else {
                    othersHasItems.add(item.getKey());
                }
            }
            System.out.println(sumXY+" "+sumX+" "+sumY);
            if(n>0){//经过了计算,执行计算pearson距离
                double denominator = Math.sqrt(sumX2 - sumX * sumX / n) * Math.sqrt(sumY2 - sumY * sumY / n);
                double pearsonDistance;
                if(denominator>threshold){
                    pearsonDistance = (sumXY - sumX * sumY / n) / denominator;
                    result.addAll(othersHasItems);
                    System.out.println("找到相似:"+userId+" "+entry.getKey()+" "+pearsonDistance);
                }
            }

        }


        List<Integer> resourceIds = new ArrayList<>(result.stream().toList());
        //推荐结果进行随机打乱,确保获得的内容不一致
        Collections.shuffle(resourceIds);
        //限制推荐数量
        resourceIds = resourceIds.stream().limit(maxRecommendNum).toList();
        return resourceIds;
    }
}
